// dbOperation
// 数据库操作单元

package main

import (
	"github.com/pkg/errors"
	mgo "gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type DBOperation struct {
	session *mgo.Session
	dbName  string
}

const (
	CollectionReceivers              string = "receivers"
	CollectionPositions              string = "positions"
	CollectionCustomers              string = "customers"
	CollectionCompanies              string = "companies"
	CollectionTeams                  string = "teams"
	CollectionOrganizationProperties string = "organizationproperties"
	CollectionTireWarnings           string = "tirewarnings"
	CollectionLastStatues            string = "laststatuses"
	CollectionWarningDeals           string = "warningdeals"
	CollectionVehicles               string = "vehicles"
)

const (
	UserTypeAdministrator int = 0
	UserTypeTeamLeader    int = 1
	UserTypeDriver        int = 2
	UserTypeIndividual    int = 3
	UserTypeSystemUser    int = 100
)

const (
	GpsWGS84 string = "wgs"
	GpsGCJ02 string = "gcj"
	GpsBd09  string = "bd"
)

func NewDBOperation(cfg *Config) (*DBOperation, error) {
	session, err := mgo.Dial(cfg.DBUrl)
	if err != nil {
		zLog(nil, "数据库连接失败::", err.Error())
		return nil, err
	}
	if cfg.DBUser != "" && cfg.DBPwd != "" {
		err = session.Login(&mgo.Credential{
			Username: cfg.DBUser,
			Password: cfg.DBPwd,
		})
		if err != nil {
			zLog(nil, "数据库登录失败::", err.Error())
			return nil, err
		}
	}
	session.SetPoolLimit(100)

	dbo := DBOperation{session, cfg.DBName}

	return &dbo, nil
}

func (p *DBOperation) Close() {
	p.session.Close()
}

func (p *DBOperation) checkAccessToken(userid string, token string) bool {
	s := p.session.Copy()
	defer s.Close()

	if !bson.IsObjectIdHex(userid) {
		return false
	}
	oi := bson.ObjectIdHex(userid)
	c := s.DB(p.dbName).C(CollectionCustomers)
	count, err := c.Find(bson.M{"_id": oi, "token": token}).Count()
	if err != nil || count == 0 {
		return false
	}
	return true
}

type CustomerT struct {
	Utype int    `bson:"utype"`
	Cid   int    `bson:"cid"`
	Tid   string `bson:"tid"`
}

func (p *DBOperation) userInfo(userId string) (*CustomerT, error) {
	if !bson.IsObjectIdHex(userId) {
		return nil, errors.New("无效的userid")
	}

	s := p.session.Copy()
	defer s.Close()

	var ct CustomerT
	oi := bson.ObjectIdHex(userId)
	c := s.DB(p.dbName).C(CollectionCustomers)
	err := c.Find(bson.M{"_id": oi}).Select(bson.M{"_id": 0, "utype": 1, "cid": 1, "tid": 1}).One(&ct)
	if err != nil {
		return nil, err
	}
	return &ct, nil
}

func (p *DBOperation) companyHasReceiver(cid int, rid string) bool {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionOrganizationProperties)
	var res interface{}

	err := c.Find(bson.M{"cid": cid, "rid": rid}).Select(bson.M{"_id": 1}).One(&res)
	return err == nil

}

func (p *DBOperation) teamHasReceiver(tid string, rid string) bool {
	if !bson.IsObjectIdHex(tid) {
		return false
	}

	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionOrganizationProperties)

	err := c.Find(bson.M{"tid": tid, "rid": rid}).Select(bson.M{"_id": 1}).One(nil)
	return err == nil
}

func (p *DBOperation) userHasReceiver(uid string, rid string) bool {
	if !bson.IsObjectIdHex(uid) {
		return false
	}

	s := p.session.Copy()
	defer s.Close()

	//var res interface{}
	c := s.DB(p.dbName).C(CollectionCustomers)
	_id := bson.ObjectIdHex(uid)
	err := c.Find(bson.M{"_id": _id, "rids": rid}).Select(bson.M{"_id": 1}).One(nil)
	return err == nil
}

// func (p *DBOperation) newPosition(rids []string, _id bson.ObjectId) []map[string]interface{} {
type PositionT struct {
	Rid string      `bson:"rid"`
	LL  PositionLLT `bson:"ll"`
}
type PositionLLT struct {
	T    int64   `bson:"t"`
	Lat  float32 `bson:"lat"`
	Lng  float32 `bson:"lng"`
	LatB float32 `bson:"latb"`
	LngB float32 `bson:"lngb"`
	LatG float32 `bson:"latg"`
	LngG float32 `bson:"lngg"`
}

func (p *DBOperation) newPosition(t int64) []PositionT { //[]map[string]interface{} {
	s := p.session.Copy()
	defer s.Close()

	var res []PositionT //[]map[string]interface{}
	var err error

	c := s.DB(p.dbName).C(CollectionLastStatues)
	err = c.Find(bson.M{"ll.t": bson.M{"$gt": t}}).Select(bson.M{"_id": 0, "rid": 1, "ll": 1}).All(&res)

	// c := s.DB(p.dbName).C(CollectionPositions)
	// if _id == "" {
	// 	err = c.Find(bson.M{"rid": bson.M{"$in": rids}}).Select(bson.M{"_id:": 1, "rid": 1, "t": 1, "lat": 1, "lng": 1, "latf": 1, "lngf": 1, "latg": 1, "lngg": 1}).
	// 		Sort("-_id").Limit(1).All(&res)
	// } else {
	// 	err = c.Find(bson.M{"_id": bson.M{"$gt": _id}, "rid": bson.M{"$in": rids}}).Select(bson.M{"_id:": 1, "rid": 1, "t": 1, "lat": 1, "lng": 1, "latf": 1, "lngf": 1, "latg": 1, "lngg": 1}).
	// 		Sort("_id").Limit(1000).All(&res)
	// }

	if err != nil {
		return nil
	} else {
		return res
	}
}

type AlarmDataT struct {
	Id       bson.ObjectId `bson:"_id"`
	Rid      string        `bson:"rid"`
	Sid      string        `bson:"sid"`
	Date     int           `bson:"date"`
	Time     int           `bson:"time"`
	CardId   int           `bson:"cardid"`
	SN       int           `bson:"sn"`
	WarnType int           `bson:"wt"`
}

func (p *DBOperation) newAlarm(_id bson.ObjectId) []AlarmDataT {
	s := p.session.Copy()
	defer s.Close()

	var res []AlarmDataT
	var err error
	c := s.DB(p.dbName).C(CollectionTireWarnings)
	if _id == "" {
		err = c.Find(nil).Select(bson.M{"_id:": 1, "rid": 1, "sid": 1, "date": 1, "time": 1, "cardid": 1, "sn": 1, "wt": 1}).
			Sort("-_id").Limit(1).All(&res)
	} else {
		err = c.Find(bson.M{"_id": bson.M{"$gt": _id}}).Select(bson.M{"_id:": 1, "rid": 1, "sid": 1, "date": 1, "time": 1, "cardid": 1, "sn": 1, "wt": 1}).
			Sort("_id").Limit(1000).All(&res)
	}

	if err != nil {
		return nil
	} else {
		return res
	}
}

type AlarmDealT struct {
	Action string `json:"action"`
	Wid    string `bson:"wid" json:"id"`
	Rid    string `bson:"rid" json:"rid"`
}

func (p *DBOperation) newAlarmDeal() []AlarmDealT {
	s := p.session.Copy()
	defer s.Close()

	var res []AlarmDealT
	c := s.DB(p.dbName).C(CollectionWarningDeals)
	err := c.Find(bson.M{"stat": 0}).Select(bson.M{"_id": 0}).Limit(100).All(&res)
	if err == nil {
		for i := 0; i < len(res); i++ {
			res[i].Action = ActionDeal
		}
	}

	return res
}

func (p DBOperation) setAlarmDealStat(wids []string, stat int) {
	s := p.session.Copy()
	defer s.Close()

	c := s.DB(p.dbName).C(CollectionWarningDeals)
	c.UpdateAll(bson.M{"wid": bson.M{"$in": wids}}, bson.M{"$set": bson.M{"stat": stat}})
}

func (p *DBOperation) getVehicleInfo(rid string) (vType int, plate string, err error) {
	type VehicleInfoT struct {
		Type  int    `bson:"type"`
		Plate string `bson:"plate"`
	}

	s := p.session.Copy()
	defer s.Close()

	var vi VehicleInfoT

	c := s.DB(p.dbName).C(CollectionVehicles)
	err = c.Find(bson.M{"rid": rid}).Select(bson.M{"_id": 0, "type": 1, "plate": 1}).One(&vi)
	if err != nil {
		return
	}

	return vi.Type, vi.Plate, nil
}
